package com.spring.nio.core.channel.handler;

import com.spring.nio.business.file.service.FileService;
import com.spring.nio.business.user.dto.UserDTO;
import com.spring.nio.business.user.param.UserQueryParam;
import com.spring.nio.business.user.service.UserService;
import com.spring.nio.common.BasicConstant;
import com.spring.nio.core.channel.context.SocketChannelContext;
import com.spring.nio.core.configuration.NioServerBootstrap;
import com.spring.nio.core.event.listener.event.ExceptionEvent;
import com.spring.nio.core.event.listener.event.FileResourceReleaseEvent;
import com.spring.nio.core.server.FileSystemServer;
import com.spring.nio.service.chat.frame.ChatMessageFrame;
import com.spring.nio.service.file.model.DownloadResult;
import com.spring.nio.service.file.model.FileTransportRequest;
import com.spring.nio.service.file.model.structure.FileTransportTaskDTO;
import com.spring.nio.service.file.task.FileDownloadStreamCallable;
import com.spring.nio.util.LocalTime;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.CollectionUtils;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Consumer;

/**
 * 通道处理器
 * @author spring
 * */

@Slf4j
public abstract class AbstractChannelHandler implements ChannelHandler {
    public static UserService userService = null;
    public static FileService fileService = null;
    /**
     * 聊天服务中ChatRealDataHandler中的函数操作集合
     * */
    private final static Map<String, Consumer<FunctionParam>> actionMap =  new ConcurrentHashMap<>();

    static {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = NioServerBootstrap.getConfig().getClassPathXmlApplicationContext();
        userService = classPathXmlApplicationContext.getBean(UserService.class);
        fileService = classPathXmlApplicationContext.getBean(FileService.class);
    }

    public static Map<String, Consumer<FunctionParam>> getActionMap() {
        return actionMap;
    }

    /**
     * 文件下载任务线程集(用于中断线程或是得到下载线程的执行结果)
     * */
    public static final Map<String, Map<String, Object>> fileDownloadThreadMap = new ConcurrentHashMap<>();

    /**
     *
     * */
    private static final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);

    /**
     * 文件下载结果线程是否启动
     * */
    private static Boolean isStartup = Boolean.FALSE;

    /**
     * 启动文件下载结果线程：用于处理文件下载线程返回结果，不执行文件通道关闭和传输通道的释放工作
     * */
    private void startFileDownloadLoopThread() {
        isStartup = Boolean.TRUE;
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            if(CollectionUtils.isEmpty(fileDownloadThreadMap)) {
                return;
            }

            // 获取每个下载任务的返回结果
            DownloadResult downloadResult = null;
            // 遍历每个下载任务
            Iterator<Map.Entry<String, Map<String, Object>>> iterator = fileDownloadThreadMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Map<String, Object>> entry = iterator.next();
                Map<String, Object> childMap = entry.getValue();
                FutureTask<DownloadResult> futureTask = ((FutureTask<DownloadResult>) childMap.get("FUTURE.TASK"));
                FileTransportTaskDTO fileTransportTaskDto = (FileTransportTaskDTO) childMap.get("TASK");

                try {
                    // 任务futureTask不为空，且任务完成，且任务结果不为空
                    if(!Objects.isNull(futureTask) && futureTask.isDone() && !Objects.isNull(downloadResult = futureTask.get())) {
                        fileTransportTaskDto.setTaskStatus(downloadResult.getIsFinished()?"FINISH":"EXCEPTION");
                        if(downloadResult.getIsFinished()) {
                            iterator.remove();
                        }
                        log.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] AbstractChannelHandler | --> 文件 = [{}] 下载结果 = [{}]", fileTransportTaskDto.getFile().getName(), downloadResult.getData());
                    }
                } catch (InterruptedException | ExecutionException e) {
                    log.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] AbstractChannelHandler | --> 文件 = [{}] 获取结果出现异常, 将终止当前文件下载结果的获取", fileTransportTaskDto.getFile().getName(), e.getCause().getMessage());
                    iterator.remove();
                }
            }
        }, 1, 5, TimeUnit.SECONDS);
    }

    /**
     * 添加文件下载任务
     * @param fileTransportTaskDto
     */
    protected void addFileDownloadTask(FileTransportTaskDTO fileTransportTaskDto) {
        synchronized (isStartup) {
            if(CollectionUtils.isEmpty(fileDownloadThreadMap) && !isStartup) {
                this.startFileDownloadLoopThread();
            }
        }

        if(!fileDownloadThreadMap.containsKey(fileTransportTaskDto.getSocketChannelContext().getRemoteAddress())) {
            FileDownloadStreamCallable callable = new FileDownloadStreamCallable(fileTransportTaskDto);
            FutureTask<DownloadResult> futureTask = new FutureTask<>(callable);

            // 添加文件下载任务
            Map<String, Object> taskMap = new HashMap<>();
            taskMap.put("FUTURE.TASK", futureTask);
            taskMap.put("CALLABLE", callable);
            taskMap.put("TAG", fileTransportTaskDto.getFileTag());
            taskMap.put("TASK", fileTransportTaskDto);
            fileDownloadThreadMap.put(fileTransportTaskDto.getSocketChannelContext().getRemoteAddress(), taskMap);

            // 启动文件下载线程
            new Thread(futureTask, BasicConstant.FILE_DOWNLOAD_THREAD).start();

            log.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] AbstractChannelHandler | --> 用户 [{}] 下载文件 [{}] 任务创建成功, 并成功启动下载线程",
                fileTransportTaskDto.getUserName(),
                fileTransportTaskDto.getFile().getName());
        }
    }

    /**
     * 文件操作取消处理(由聊天通道进行取消操作)
     * @param fileTransportRequest
     * @return
     */
    public static void cancelFileTransport(FileTransportRequest fileTransportRequest) {
        // 获取文件关闭通道类型(上行关闭还是下行关闭)
        String operateType = fileTransportRequest.getOperate().split(";")[1];

        // 查询当前用户的文件传输集合
        if(StringUtils.equals(operateType, "UPLOAD")) {
            UserQueryParam userQueryParam = new UserQueryParam();
            userQueryParam.setUserId(fileTransportRequest.getLaunchUserId());
            UserDTO userDto = userService.getOnlineUserById(userQueryParam);
            FileTransportTaskDTO fileTransportTaskDto = userDto.getUploadFileMap().remove(fileTransportRequest.getFileTransportAddress());
            fileTransportTaskDto.setRestTaskCount(userDto.getUploadFileMap().size());
            fileTransportTaskDto.setTaskStatus("CANCEL");
            // 此处采取重试进行关闭文件通道以及删除文件
            handlerFileRelease(fileTransportTaskDto, Boolean.FALSE);
        } else {
            // 下载操作中断下载线程
            Map<String, Object> taskMap = fileDownloadThreadMap.remove(fileTransportRequest.getFileTransportAddress());
            FileTransportTaskDTO fileTransportTaskDto = (FileTransportTaskDTO) taskMap.get("TASK");
            FileDownloadStreamCallable callable = (FileDownloadStreamCallable) taskMap.get("CALLABLE");
            if(!Objects.isNull(fileTransportTaskDto)) {
                // 上传成功，释放待上传文件占用资源,即删除userDto中map对应的key-value
                try {
                    fileTransportTaskDto.setTaskStatus("CANCEL");
                    // 终止下载线程
                    while (!callable.getStop()) {
                        callable.setStop(Boolean.TRUE);
                    }
                    // 关闭文件下载时的文件读取通道
                    FileSystemServer.closeFileChannel(fileTransportTaskDto.getFileChannel());
                    // 清空文件下载通道数据(在短时间内该通道再次触发的写事件不会在发送数据给客户端)以及关闭文件下载传输通道
                    fileTransportTaskDto.getSocketChannelContext().getBlockingQueue().clear();
                    fileTransportTaskDto.getSocketChannelContext().getSocketChannel().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            log.info("[ " + LocalTime.formatDate(LocalDateTime.now()) + " ] AbstractChannelHandler | --> 用户 [{}] 成功移除下载文件任务,下载任务线程已终止, 文件通道成功, 下载通道已关闭, 剩余下载任务数 = [{}], 文件名 = {}", fileTransportRequest.getLaunchUserName(), fileDownloadThreadMap.size(), fileTransportTaskDto.getFile().getName());
        }
    }

    /**
     * 处理文件通道关闭删除文件释放资源
     * @param obj
     * @param isSend 是否向客户端发送异常信息
     * */
    public static void handlerFileRelease(Object obj, Boolean isSend) {
        FileResourceReleaseEvent.FileEventSource source = new FileResourceReleaseEvent.FileEventSource();
        source.setObj(obj);
        source.setIsSend(isSend);
        NioServerBootstrap.getConfig().getClassPathXmlApplicationContext().publishEvent(new FileResourceReleaseEvent(source));
    }

    @Data
    public static class FunctionParam {
        private SocketChannelContext socketChannelContext;
        private ChatMessageFrame chatMessageFrame;
    }

}
